#include "UsbCan.h"
#include <QDebug>
#include <iostream>

namespace zlg
{
UsbCan::UsbCan()
    : itsDeviceHandle(NULL), itsChannelHandle(NULL), itsChannelIndex(0), itsDeviceIndex(0), itsDeviceTypeIndex(0)
    , itsStatus(CLOSE), itsCustomBaudrate(), itsBaudrate()
    , itsUsbCanAbitDef(UsbCanAbitDef::UsbCanAbit_100kbps), itsUsbCanDbitDef(UsbCanDbitDef::UsbCanDbit_5Mbps), itsResistanceEnable(0)
    , itsRecvThread(nullptr), itsRecvCbs()
{

}
UsbCan::UsbCan(int deviceType, int device, int channel, const BaudrateDef& baudrate, const UsbCanAbitDef usbAbit, const UsbCanDbitDef usbDbit, bool resistanceEnable)
    : itsDeviceHandle(NULL), itsChannelHandle(NULL), itsChannelIndex(channel), itsDeviceIndex(device), itsDeviceTypeIndex(deviceType)
    , itsStatus(CLOSE), itsCustomBaudrate(), itsBaudrate(baudrate)
    , itsUsbCanAbitDef(usbAbit), itsUsbCanDbitDef(usbDbit), itsResistanceEnable(resistanceEnable)
    , itsRecvThread(nullptr), itsRecvCbs()
{
    if(itsBaudrate == BaudrateDef::Baudrate_0)
    {
        abort();
    }
}

UsbCan::UsbCan(int deviceType, int device, int channel, const std::string& customBaudrate, bool resistanceEnable)
    : itsDeviceHandle(NULL), itsChannelHandle(NULL), itsChannelIndex(channel), itsDeviceIndex(device), itsDeviceTypeIndex(deviceType)
    , itsStatus(CLOSE), itsCustomBaudrate(customBaudrate), itsBaudrate()
    , itsUsbCanAbitDef(UsbCanAbitDef::UsbCanAbit_100kbps), itsUsbCanDbitDef(UsbCanDbitDef::UsbCanDbit_5Mbps), itsResistanceEnable(resistanceEnable)
    , itsRecvThread(nullptr), itsRecvCbs()
{

}

UsbCan::~UsbCan()
{
    itsStatus = CLOSE;
    if(itsRecvThread && itsRecvThread->joinable())
    {
        itsRecvThread->join();
        itsRecvThread = nullptr;
    }
}

void UsbCan::configCanDevice(int deviceType, int device, int channel, const BaudrateDef& baudrate, const UsbCanAbitDef usbAbit, const UsbCanDbitDef usbDbit, bool resistanceEnable)
{
    itsChannelIndex = channel;
    itsDeviceIndex = device;
    itsDeviceTypeIndex = deviceType;
    itsStatus = CLOSE;
    itsBaudrate = baudrate;
    itsUsbCanAbitDef = usbAbit;
    itsUsbCanDbitDef = usbDbit;
    itsResistanceEnable = resistanceEnable;
}

void UsbCan::configCanDevice(int deviceType, int device, int channel, const std::string& customBaudrate, bool resistanceEnable)
{
    itsChannelIndex = channel;
    itsDeviceIndex = device;
    itsDeviceTypeIndex = deviceType;
    itsCustomBaudrate = customBaudrate;
    itsResistanceEnable = resistanceEnable;
}

OperationResult UsbCan::OpenCan()
{
    auto result = openDevice();
    if(result == OperationResult::Success)
    {
        result = initCan();
        if(result == OperationResult::Success)
        {
            result = startCan();
        }
    }
    return result;
}

OperationResult UsbCan::CloseCan()
{
    itsStatus = CLOSE;
    if(itsRecvThread && itsRecvThread->joinable())
    {
        itsRecvThread->join();
        itsRecvThread = nullptr;
    }

    auto result = resetCan();
    if(result == OperationResult::Success)
    {
        result = closeCan();
    }
    return result;
}

bool UsbCan::IsDeviceOnLine()
{
    bool ret = ZCAN_IsDeviceOnLine(itsDeviceHandle) > 0;
    return ret;
}

OperationResult UsbCan::SendCanMsg(const uint32_t canId, const SendType& sendType, const std::vector<uint8_t>& data)
{
    return itsStatus == RUNNING ? sendData(buildCanFrame(canId, sendType, data)) : OperationResult::DeviceIsNotOpen;
}

OperationResult UsbCan::SendCanfdMsg(const uint32_t canId, const SendType& sendType, const std::vector<uint8_t>& data)
{
    return itsStatus == RUNNING ? sendData(buildCanfdFrame(canId, sendType, data, true)) : OperationResult::DeviceIsNotOpen;
}

void UsbCan::reigsterListenerCb(std::function<void(ZCAN_Receive_Data*)> cb)
{
    itsRecvCbs.emplace_back(std::move(cb));
}

OperationResult UsbCan::openDevice()
{
    itsDeviceHandle = ZCAN_OpenDevice(kDeviceType[itsDeviceTypeIndex].device_type, itsDeviceIndex, 0);
    if (INVALID_DEVICE_HANDLE == itsDeviceHandle)
    {
        qDebug() << "打开设备失败!";
        return OperationResult::Fail_UnknownReason;
    }
    itsStatus = IDLE;
    qDebug() << "打开设备成功!";
    return OperationResult::Success;
}

OperationResult UsbCan::initCan()
{
    if (itsStatus != IDLE)
    {
        qDebug() << "设备还没打开!";
        return OperationResult::DeviceIsNotOpen;
    }
    ZCAN_CHANNEL_INIT_CONFIG config;
    memset(&config, 0, sizeof(config));

    UINT type = kDeviceType[itsDeviceTypeIndex].device_type;
    const bool usbcanfd = type==ZCAN_USBCANFD_100U ||
                          type==ZCAN_USBCANFD_200U ||
                          type==ZCAN_USBCANFD_MINI;
    const bool pciecanfd = type==ZCAN_PCIE_CANFD_100U ||
                           type == ZCAN_PCIE_CANFD_200U ||
                           type == ZCAN_PCIE_CANFD_400U_EX;
    const bool canfdDevice = usbcanfd || pciecanfd;

    //设置波特率
    if (itsCustomBaudrate.empty())
    {
        if (!canfdDevice && !SetBaudrate())
        {
            qDebug() << "设置波特率失败!";
            return OperationResult::SetBaudrateFail;
        }
    }
    else
    {
        if (!SetCustomBaudrate())
        {
            qDebug() << "设置自定义波特率失败!";
            return OperationResult::SetCustomBaudrateFail;
        }
    }

    if (usbcanfd)
    {
        char path[50] = {0};
        char value[100] = {0};
        sprintf_s(path, "%d/canfd_standard", itsChannelIndex);
        sprintf_s(value, "%d", 0);
        ZCAN_SetValue(itsDeviceHandle, path, value);

        //设置波特率
        if (!itsCustomBaudrate.empty())
        {
            if (!SetCustomBaudrate())
            {
                qDebug() << "设置自定义波特率失败!";
                return OperationResult::SetBaudrateFail;
            }
        }
        else
        {
            if (!SetCanfdBaudrate())
            {
                qDebug() << "设置CANFD波特率失败!";
                return OperationResult::SetUsbCanFdBaudrateFail;
            }
        }
        config.can_type = TYPE_CANFD;
        config.canfd.mode = NORMAL_WORK_MODE;
        config.canfd.filter = SINGLE_WAVE_FILTER;
        config.canfd.acc_code = 0;          //验收码
        config.canfd.acc_mask = 0xffffffff; //屏蔽码

    }
    else if (pciecanfd)
    {

        char path[50] = { 0 };
        char value[100] = { 0 };
        if (!SetCanfdBaudrate())
        {
            qDebug() << "设置CANFD波特率失败!";
            return OperationResult::SetUsbCanFdBaudrateFail;
        }

        if (type == ZCAN_PCIE_CANFD_400U_EX )
            {
            sprintf_s(path, "0/set_device_recv_merge");
            sprintf_s(value, "0");
            ZCAN_SetValue(itsDeviceHandle, path, value);
            }

        config.can_type = TYPE_CANFD;
        config.canfd.mode = TYPE_CANFD;
        config.canfd.filter = SINGLE_WAVE_FILTER;
        config.canfd.acc_code = 0;          //验收码
        config.canfd.acc_mask = 0xffffffff; //屏蔽码

    }
    else
    {
        config.can_type = TYPE_CAN;
        config.canfd.mode = NORMAL_WORK_MODE;
        config.canfd.filter = SINGLE_WAVE_FILTER;
        config.canfd.acc_code = 0;          //验收码
        config.canfd.acc_mask = 0xffffffff; //屏蔽码
    }

    itsChannelHandle = ZCAN_InitCAN(itsDeviceHandle, itsChannelIndex, &config);
    if (INVALID_CHANNEL_HANDLE == itsChannelHandle)
    {
        qDebug() << "初始化CAN失败!";
        return OperationResult::InitCanFail;
    }
    if (usbcanfd)
    {
        if (itsResistanceEnable && !SetResistanceEnable())
        {
            qDebug() << "设置终端电阻失败!";
            return OperationResult::SetResistanceEnableFail;
        }
    }
    qDebug() << "初始化成功!";
    return OperationResult::Success;
}
OperationResult UsbCan::startCan()
{
    if (ZCAN_StartCAN(itsChannelHandle) != STATUS_OK)
    {
        qDebug() << "启动CAN失败!";
        return OperationResult::StartFail;
    }

    itsStatus = RUNNING;
    itsRecvThread = std::make_unique<std::thread>([this](){
        recvDataProcess();
    });

    qDebug() << "启动CAN成功!";
    return OperationResult::Success;
}

OperationResult UsbCan::resetCan()
{
    if (ZCAN_ResetCAN(itsChannelHandle) != STATUS_OK)
    {
        qDebug() << "复位失败!";
        return OperationResult::ResteFail;
    }
    itsStatus = IDLE;
    qDebug() << "复位成功!";
    return OperationResult::Success;
}
OperationResult UsbCan::closeCan()
{
    ZCAN_ResetCAN(itsChannelHandle);
    ZCAN_CloseDevice(itsDeviceHandle);
    qDebug() << "设备已关闭!";
    return OperationResult::Success;
}

bool UsbCan::SetCustomBaudrate()
{
    char path[50] = {0};
    char value[100] = {0};
    sprintf_s(path, "%d/baud_rate_custom", itsChannelIndex);
    sprintf_s(value, "%s", itsCustomBaudrate.data());
    return 1 == ZCAN_SetValue(itsDeviceHandle, path, value);
}

bool UsbCan::SetBaudrate()
{

    char path[50] = {0};
    sprintf_s(path, "%d/baud_rate", itsChannelIndex);
    char value[10] = {0};
    sprintf_s(value, "%d", kBaudrate.at(itsBaudrate));
    return 1 == ZCAN_SetValue(itsDeviceHandle, path, value);
}

bool UsbCan::SetCanfdBaudrate()
{

    char path[50] = { 0 };
    sprintf_s(path, "%d/canfd_abit_baud_rate", itsChannelIndex);
    char value[10] = { 0 };
    sprintf_s(value, "%d", kAbitTimingUSB.at(itsUsbCanAbitDef));//仲裁域波特率
    int ret_a = ZCAN_SetValue(itsDeviceHandle, path, value);

    sprintf_s(path, "%d/canfd_dbit_baud_rate", itsChannelIndex);
    sprintf_s(value, "%d", kDbitTimingUSB.at(itsUsbCanDbitDef));//数据域波特率
    int ret_d = ZCAN_SetValue(itsDeviceHandle, path, value);
    return 1 == (ret_a&&ret_d);
}

bool UsbCan::SetResistanceEnable()
{

    char path[50] = {0};
    sprintf_s(path, "%d/initenal_resistance", itsChannelIndex);
    char value[10] = {0};
    sprintf_s(value, "%d", itsResistanceEnable);
    return 1 == ZCAN_SetValue(itsDeviceHandle, path, value);
}

void UsbCan::recvDataProcess()
{
    while(itsStatus == RUNNING)
    {
        onRecvData();
    }
}
void UsbCan::onRecvData()
{
    ZCAN_Receive_Data can_data[ICan::MAX_RECV_SIZE];
    if (ZCAN_GetReceiveNum(itsChannelHandle, TYPE_CAN) > 0)
    {
        ZCAN_Receive(itsChannelHandle, can_data, ICan::MAX_RECV_SIZE, 50);//return len
        for(auto cb : itsRecvCbs)
        {
            cb(can_data);
        }
    }

    ZCAN_ReceiveFD_Data canfd_data[ICan::MAX_RECV_SIZE];
    if (ZCAN_GetReceiveNum(itsChannelHandle, TYPE_CANFD) > 0)
    {
        ZCAN_ReceiveFD(itsChannelHandle, canfd_data, 100, 50);//return len
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

std::unique_ptr<ZCAN_Transmit_Data> UsbCan::buildCanFrame(const uint32_t canId, const SendType& sendType, const std::vector<uint8_t>& data)
{
    std::unique_ptr<ZCAN_Transmit_Data> canFrame = std::make_unique<ZCAN_Transmit_Data>();
    memset(canFrame.get(), 0, sizeof(ZCAN_Transmit_Data));
    size_t size = data.size() > CAN_FRAME_MAX_SIZE ? CAN_FRAME_MAX_SIZE : data.size();
    canFrame->transmit_type = static_cast<UINT>(sendType);
    canFrame->frame.can_dlc = static_cast<BYTE>(data.size());
    canFrame->frame.can_id = canId;
    std::copy(data.begin(), data.begin()+size, canFrame->frame.data);
    return canFrame;
}

OperationResult UsbCan::sendData(std::unique_ptr<ZCAN_Transmit_Data> canFrame)
{
    auto result = ZCAN_Transmit(itsChannelHandle, canFrame.get(), 1);
    return result > 0 ? OperationResult::Success : OperationResult::SendCanDataFail;
}

std::unique_ptr<ZCAN_TransmitFD_Data> UsbCan::buildCanfdFrame(const uint32_t canId, const SendType& sendType, const std::vector<uint8_t>& data, bool canfdBRS)
{
    std::unique_ptr<ZCAN_TransmitFD_Data> canFrame = std::make_unique<ZCAN_TransmitFD_Data>();
    memset(canFrame.get(), 0, sizeof(ZCAN_TransmitFD_Data));
    size_t size = data.size() > CANFD_FRAME_MAX_SIZE ? CANFD_FRAME_MAX_SIZE : data.size();
    canFrame->transmit_type = static_cast<UINT>(sendType);
    canFrame->frame.len = static_cast<BYTE>(data.size());
    canFrame->frame.can_id = canId;
    canFrame->frame.flags = canfdBRS ? 1 : 0;
    std::copy(data.begin(), data.begin()+size, canFrame->frame.data);
    return canFrame;
}

OperationResult UsbCan::sendData(std::unique_ptr<ZCAN_TransmitFD_Data> canFrame)
{
    auto result = ZCAN_TransmitFD(itsChannelHandle, canFrame.get(), 1);
    return result > 0 ? OperationResult::Success : OperationResult::SendCanDataFail;
}

}
